added some development tools
[windows-sources.git] / developer / Samples / NET Standard / ParallelExtensionsExtras / Extensions / TaskExtrasExtensions.cs
blob1e161a22224f610d35186cc3c67221b9956a768a
1 //--------------------------------------------------------------------------
2 //
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 //
5 // File: TaskExtensions.cs
6 //
7 //--------------------------------------------------------------------------
9 using System.Linq;
10 using System.Windows.Threading;
12 namespace System.Threading.Tasks
14 /// <summary>Extensions methods for Task.</summary>
15 public static class TaskExtrasExtensions
17 #region ContinueWith accepting TaskFactory
18 /// <summary>Creates a continuation task using the specified TaskFactory.</summary>
19 /// <param name="task">The antecedent Task.</param>
20 /// <param name="continuationAction">The continuation action.</param>
21 /// <param name="factory">The TaskFactory.</param>
22 /// <returns>A continuation task.</returns>
23 public static Task ContinueWith(
24 this Task task, Action<Task> continuationAction, TaskFactory factory)
26 return task.ContinueWith(continuationAction, factory.CancellationToken, factory.ContinuationOptions, factory.Scheduler);
29 /// <summary>Creates a continuation task using the specified TaskFactory.</summary>
30 /// <param name="task">The antecedent Task.</param>
31 /// <param name="continuationFunction">The continuation function.</param>
32 /// <param name="factory">The TaskFactory.</param>
33 /// <returns>A continuation task.</returns>
34 public static Task<TResult> ContinueWith<TResult>(
35 this Task task, Func<Task, TResult> continuationFunction, TaskFactory factory)
37 return task.ContinueWith(continuationFunction, factory.CancellationToken, factory.ContinuationOptions, factory.Scheduler);
39 #endregion
41 #region ContinueWith accepting TaskFactory<TResult>
42 /// <summary>Creates a continuation task using the specified TaskFactory.</summary>
43 /// <param name="task">The antecedent Task.</param>
44 /// <param name="continuationAction">The continuation action.</param>
45 /// <param name="factory">The TaskFactory.</param>
46 /// <returns>A continuation task.</returns>
47 public static Task ContinueWith<TResult>(
48 this Task<TResult> task, Action<Task<TResult>> continuationAction, TaskFactory<TResult> factory)
50 return task.ContinueWith(continuationAction, factory.CancellationToken, factory.ContinuationOptions, factory.Scheduler);
53 /// <summary>Creates a continuation task using the specified TaskFactory.</summary>
54 /// <param name="task">The antecedent Task.</param>
55 /// <param name="continuationFunction">The continuation function.</param>
56 /// <param name="factory">The TaskFactory.</param>
57 /// <returns>A continuation task.</returns>
58 public static Task<TNewResult> ContinueWith<TResult, TNewResult>(
59 this Task<TResult> task, Func<Task<TResult>, TNewResult> continuationFunction, TaskFactory<TResult> factory)
61 return task.ContinueWith(continuationFunction, factory.CancellationToken, factory.ContinuationOptions, factory.Scheduler);
63 #endregion
65 #region ToAsync(AsyncCallback, object)
66 /// <summary>
67 /// Creates a Task that represents the completion of another Task, and
68 /// that schedules an AsyncCallback to run upon completion.
69 /// </summary>
70 /// <param name="task">The antecedent Task.</param>
71 /// <param name="callback">The AsyncCallback to run.</param>
72 /// <param name="state">The object state to use with the AsyncCallback.</param>
73 /// <returns>The new task.</returns>
74 public static Task ToAsync(this Task task, AsyncCallback callback, object state)
76 if (task == null) throw new ArgumentNullException("task");
78 var tcs = new TaskCompletionSource<object>(state);
79 task.ContinueWith(_ =>
81 tcs.SetFromTask(task);
82 if (callback != null) callback(tcs.Task);
83 });
84 return tcs.Task;
87 /// <summary>
88 /// Creates a Task that represents the completion of another Task, and
89 /// that schedules an AsyncCallback to run upon completion.
90 /// </summary>
91 /// <param name="task">The antecedent Task.</param>
92 /// <param name="callback">The AsyncCallback to run.</param>
93 /// <param name="state">The object state to use with the AsyncCallback.</param>
94 /// <returns>The new task.</returns>
95 public static Task<TResult> ToAsync<TResult>(this Task<TResult> task, AsyncCallback callback, object state)
97 if (task == null) throw new ArgumentNullException("task");
99 var tcs = new TaskCompletionSource<TResult>(state);
100 task.ContinueWith(_ =>
102 tcs.SetFromTask(task);
103 if (callback != null) callback(tcs.Task);
105 return tcs.Task;
107 #endregion
109 #region Exception Handling
110 /// <summary>Suppresses default exception handling of a Task that would otherwise reraise the exception on the finalizer thread.</summary>
111 /// <param name="task">The Task to be monitored.</param>
112 /// <returns>The original Task.</returns>
113 public static Task IgnoreExceptions(this Task task)
115 task.ContinueWith(t => { var ignored = t.Exception; },
116 CancellationToken.None,
117 TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnFaulted,
118 TaskScheduler.Default);
119 return task;
122 /// <summary>Suppresses default exception handling of a Task that would otherwise reraise the exception on the finalizer thread.</summary>
123 /// <param name="task">The Task to be monitored.</param>
124 /// <returns>The original Task.</returns>
125 public static Task<T> IgnoreExceptions<T>(this Task<T> task)
127 return (Task<T>)((Task)task).IgnoreExceptions();
130 /// <summary>Fails immediately when an exception is encountered.</summary>
131 /// <param name="task">The Task to be monitored.</param>
132 /// <returns>The original Task.</returns>
133 public static Task FailFastOnException(this Task task)
135 task.ContinueWith(t => Environment.FailFast("A task faulted.", t.Exception),
136 CancellationToken.None,
137 TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnFaulted,
138 TaskScheduler.Default);
139 return task;
142 /// <summary>Fails immediately when an exception is encountered.</summary>
143 /// <param name="task">The Task to be monitored.</param>
144 /// <returns>The original Task.</returns>
145 public static Task<T> FailFastOnException<T>(this Task<T> task)
147 return (Task<T>)((Task)task).FailFastOnException();
150 /// <summary>Propagates any exceptions that occurred on the specified task.</summary>
151 /// <param name="task">The Task whose exceptions are to be propagated.</param>
152 public static void PropagateExceptions(this Task task)
154 if (!task.IsCompleted) throw new InvalidOperationException("The task has not completed.");
155 if (task.IsFaulted) task.Wait();
158 /// <summary>Propagates any exceptions that occurred on the specified tasks.</summary>
159 /// <param name="task">The Tassk whose exceptions are to be propagated.</param>
160 public static void PropagateExceptions(this Task [] tasks)
162 if (tasks == null) throw new ArgumentNullException("tasks");
163 if (tasks.Any(t => t == null)) throw new ArgumentException("tasks");
164 if (tasks.Any(t => !t.IsCompleted)) throw new InvalidOperationException("A task has not completed.");
165 Task.WaitAll(tasks);
167 #endregion
169 #region Observables
170 /// <summary>Creates an IObservable that represents the completion of a Task.</summary>
171 /// <typeparam name="TResult">Specifies the type of data returned by the Task.</typeparam>
172 /// <param name="task">The Task to be represented as an IObservable.</param>
173 /// <returns>An IObservable that represents the completion of the Task.</returns>
174 public static IObservable<TResult> ToObservable<TResult>(this Task<TResult> task)
176 if (task == null) throw new ArgumentNullException("task");
177 return new TaskObservable<TResult> { _task = task };
180 /// <summary>An implementation of IObservable that wraps a Task.</summary>
181 /// <typeparam name="TResult">The type of data returned by the task.</typeparam>
182 private class TaskObservable<TResult> : IObservable<TResult>
184 internal Task<TResult> _task;
186 public IDisposable Subscribe(IObserver<TResult> observer)
188 // Validate arguments
189 if (observer == null) throw new ArgumentNullException("observer");
191 // Support cancelling the continuation if the observer is unsubscribed
192 var cts = new CancellationTokenSource();
194 // Create a continuation to pass data along to the observer
195 _task.ContinueWith(t =>
197 switch (t.Status)
199 case TaskStatus.RanToCompletion:
200 observer.OnNext(_task.Result);
201 observer.OnCompleted();
202 break;
204 case TaskStatus.Faulted:
205 observer.OnError(_task.Exception);
206 break;
208 case TaskStatus.Canceled:
209 observer.OnError(new TaskCanceledException(t));
210 break;
212 }, cts.Token);
214 // Support unsubscribe simply by canceling the continuation if it hasn't yet run
215 return new CancelOnDispose { Source = cts };
219 /// <summary>Translate a call to IDisposable.Dispose to a CancellationTokenSource.Cancel.</summary>
220 private class CancelOnDispose : IDisposable
222 internal CancellationTokenSource Source;
223 void IDisposable.Dispose() { Source.Cancel(); }
225 #endregion
227 #region Timeouts
228 /// <summary>Creates a new Task that mirrors the supplied task but that will be canceled after the specified timeout.</summary>
229 /// <typeparam name="TResult">Specifies the type of data contained in the task.</typeparam>
230 /// <param name="task">The task.</param>
231 /// <param name="timeout">The timeout.</param>
232 /// <returns>The new Task that may time out.</returns>
233 public static Task WithTimeout(this Task task, TimeSpan timeout)
235 var result = new TaskCompletionSource<object>(task.AsyncState);
236 var timer = new Timer(state => ((TaskCompletionSource<object>)state).TrySetCanceled(), result, timeout, TimeSpan.FromMilliseconds(-1));
237 task.ContinueWith(t =>
239 timer.Dispose();
240 result.TrySetFromTask(t);
241 }, TaskContinuationOptions.ExecuteSynchronously);
242 return result.Task;
245 /// <summary>Creates a new Task that mirrors the supplied task but that will be canceled after the specified timeout.</summary>
246 /// <typeparam name="TResult">Specifies the type of data contained in the task.</typeparam>
247 /// <param name="task">The task.</param>
248 /// <param name="timeout">The timeout.</param>
249 /// <returns>The new Task that may time out.</returns>
250 public static Task<TResult> WithTimeout<TResult>(this Task<TResult> task, TimeSpan timeout)
252 var result = new TaskCompletionSource<TResult>(task.AsyncState);
253 var timer = new Timer(state => ((TaskCompletionSource<TResult>)state).TrySetCanceled(), result, timeout, TimeSpan.FromMilliseconds(-1));
254 task.ContinueWith(t =>
256 timer.Dispose();
257 result.TrySetFromTask(t);
258 }, TaskContinuationOptions.ExecuteSynchronously);
259 return result.Task;
261 #endregion
263 #region Children
264 /// <summary>
265 /// Ensures that a parent task can't transition into a completed state
266 /// until the specified task has also completed, even if it's not
267 /// already a child task.
268 /// </summary>
269 /// <param name="task">The task to attach to the current task as a child.</param>
270 public static void AttachToParent(this Task task)
272 if (task == null) throw new ArgumentNullException("task");
273 task.ContinueWith(t => t.Wait(), CancellationToken.None,
274 TaskContinuationOptions.AttachedToParent |
275 TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
277 #endregion
279 #region Waiting
280 /// <summary>Waits for the task to complete execution, pumping in the meantime.</summary>
281 /// <param name="task">The task for which to wait.</param>
282 /// <remarks>This method is intended for usage with Windows Presentation Foundation.</remarks>
283 public static void WaitWithPumping(this Task task)
285 if (task == null) throw new ArgumentNullException("task");
286 var nestedFrame = new DispatcherFrame();
287 task.ContinueWith(_ => nestedFrame.Continue = false);
288 Dispatcher.PushFrame(nestedFrame);
289 task.Wait();
292 /// <summary>Waits for the task to complete execution, returning the task's final status.</summary>
293 /// <param name="task">The task for which to wait.</param>
294 /// <returns>The completion status of the task.</returns>
295 /// <remarks>Unlike Wait, this method will not throw an exception if the task ends in the Faulted or Canceled state.</remarks>
296 public static TaskStatus WaitForCompletionStatus(this Task task)
298 if (task == null) throw new ArgumentNullException("task");
299 ((IAsyncResult)task).AsyncWaitHandle.WaitOne();
300 return task.Status;
302 #endregion
304 #region Then
305 /// <summary>Creates a task that represents the completion of a follow-up action when a task completes.</summary>
306 /// <param name="task">The task.</param>
307 /// <param name="next">The action to run when the task completes.</param>
308 /// <returns>The task that represents the completion of both the task and the action.</returns>
309 public static Task Then(this Task task, Action next)
311 if (task == null) throw new ArgumentNullException("task");
312 if (next == null) throw new ArgumentNullException("next");
314 var tcs = new TaskCompletionSource<object>();
315 task.ContinueWith(delegate
317 if (task.IsFaulted) tcs.TrySetException(task.Exception.InnerExceptions);
318 else if (task.IsCanceled) tcs.TrySetCanceled();
319 else
323 next();
324 tcs.TrySetResult(null);
326 catch (Exception exc) { tcs.TrySetException(exc); }
328 }, TaskScheduler.Default);
329 return tcs.Task;
332 /// <summary>Creates a task that represents the completion of a follow-up function when a task completes.</summary>
333 /// <param name="task">The task.</param>
334 /// <param name="next">The function to run when the task completes.</param>
335 /// <returns>The task that represents the completion of both the task and the function.</returns>
336 public static Task<TResult> Then<TResult>(this Task task, Func<TResult> next)
338 if (task == null) throw new ArgumentNullException("task");
339 if (next == null) throw new ArgumentNullException("next");
341 var tcs = new TaskCompletionSource<TResult>();
342 task.ContinueWith(delegate
344 if (task.IsFaulted) tcs.TrySetException(task.Exception.InnerExceptions);
345 else if (task.IsCanceled) tcs.TrySetCanceled();
346 else
350 var result = next();
351 tcs.TrySetResult(result);
353 catch (Exception exc) { tcs.TrySetException(exc); }
355 }, TaskScheduler.Default);
356 return tcs.Task;
359 /// <summary>Creates a task that represents the completion of a follow-up action when a task completes.</summary>
360 /// <param name="task">The task.</param>
361 /// <param name="next">The action to run when the task completes.</param>
362 /// <returns>The task that represents the completion of both the task and the action.</returns>
363 public static Task Then<TResult>(this Task<TResult> task, Action<TResult> next)
365 if (task == null) throw new ArgumentNullException("task");
366 if (next == null) throw new ArgumentNullException("next");
368 var tcs = new TaskCompletionSource<object>();
369 task.ContinueWith(delegate
371 if (task.IsFaulted) tcs.TrySetException(task.Exception.InnerExceptions);
372 else if (task.IsCanceled) tcs.TrySetCanceled();
373 else
377 next(task.Result);
378 tcs.TrySetResult(null);
380 catch (Exception exc) { tcs.TrySetException(exc); }
382 }, TaskScheduler.Default);
383 return tcs.Task;
386 /// <summary>Creates a task that represents the completion of a follow-up function when a task completes.</summary>
387 /// <param name="task">The task.</param>
388 /// <param name="next">The function to run when the task completes.</param>
389 /// <returns>The task that represents the completion of both the task and the function.</returns>
390 public static Task<TNewResult> Then<TResult, TNewResult>(this Task<TResult> task, Func<TResult, TNewResult> next)
392 if (task == null) throw new ArgumentNullException("task");
393 if (next == null) throw new ArgumentNullException("next");
395 var tcs = new TaskCompletionSource<TNewResult>();
396 task.ContinueWith(delegate
398 if (task.IsFaulted) tcs.TrySetException(task.Exception.InnerExceptions);
399 else if (task.IsCanceled) tcs.TrySetCanceled();
400 else
404 var result = next(task.Result);
405 tcs.TrySetResult(result);
407 catch (Exception exc) { tcs.TrySetException(exc); }
409 }, TaskScheduler.Default);
410 return tcs.Task;
413 /// <summary>Creates a task that represents the completion of a second task when a first task completes.</summary>
414 /// <param name="task">The first task.</param>
415 /// <param name="next">The function that produces the second task.</param>
416 /// <returns>The task that represents the completion of both the first and second task.</returns>
417 public static Task Then(this Task task, Func<Task> next)
419 if (task == null) throw new ArgumentNullException("task");
420 if (next == null) throw new ArgumentNullException("next");
422 var tcs = new TaskCompletionSource<object>();
423 task.ContinueWith(delegate
425 // When the first task completes, if it faulted or was canceled, bail
426 if (task.IsFaulted) tcs.TrySetException(task.Exception.InnerExceptions);
427 else if (task.IsCanceled) tcs.TrySetCanceled();
428 else
430 // Otherwise, get the next task. If it's null, bail. If not,
431 // when it's done we'll have our result.
432 try { next().ContinueWith(t => tcs.TrySetFromTask(t), TaskScheduler.Default); }
433 catch (Exception exc) { tcs.TrySetException(exc); }
435 }, TaskScheduler.Default);
436 return tcs.Task;
439 /// <summary>Creates a task that represents the completion of a second task when a first task completes.</summary>
440 /// <param name="task">The first task.</param>
441 /// <param name="next">The function that produces the second task based on the result of the first task.</param>
442 /// <returns>The task that represents the completion of both the first and second task.</returns>
443 public static Task Then<T>(this Task<T> task, Func<T, Task> next)
445 if (task == null) throw new ArgumentNullException("task");
446 if (next == null) throw new ArgumentNullException("next");
448 var tcs = new TaskCompletionSource<object>();
449 task.ContinueWith(delegate
451 // When the first task completes, if it faulted or was canceled, bail
452 if (task.IsFaulted) tcs.TrySetException(task.Exception.InnerExceptions);
453 else if (task.IsCanceled) tcs.TrySetCanceled();
454 else
456 // Otherwise, get the next task. If it's null, bail. If not,
457 // when it's done we'll have our result.
458 try { next(task.Result).ContinueWith(t => tcs.TrySetFromTask(t), TaskScheduler.Default); }
459 catch (Exception exc) { tcs.TrySetException(exc); }
461 }, TaskScheduler.Default);
462 return tcs.Task;
465 /// <summary>Creates a task that represents the completion of a second task when a first task completes.</summary>
466 /// <param name="task">The first task.</param>
467 /// <param name="next">The function that produces the second task.</param>
468 /// <returns>The task that represents the completion of both the first and second task.</returns>
469 public static Task<TResult> Then<TResult>(this Task task, Func<Task<TResult>> next)
471 if (task == null) throw new ArgumentNullException("task");
472 if (next == null) throw new ArgumentNullException("next");
474 var tcs = new TaskCompletionSource<TResult>();
475 task.ContinueWith(delegate
477 // When the first task completes, if it faulted or was canceled, bail
478 if (task.IsFaulted) tcs.TrySetException(task.Exception.InnerExceptions);
479 else if (task.IsCanceled) tcs.TrySetCanceled();
480 else
482 // Otherwise, get the next task. If it's null, bail. If not,
483 // when it's done we'll have our result.
484 try { next().ContinueWith(t => tcs.TrySetFromTask(t), TaskScheduler.Default); }
485 catch (Exception exc) { tcs.TrySetException(exc); }
487 }, TaskScheduler.Default);
488 return tcs.Task;
491 /// <summary>Creates a task that represents the completion of a second task when a first task completes.</summary>
492 /// <param name="task">The first task.</param>
493 /// <param name="next">The function that produces the second task based on the result of the first.</param>
494 /// <returns>The task that represents the completion of both the first and second task.</returns>
495 public static Task<TNewResult> Then<TResult, TNewResult>(this Task<TResult> task, Func<TResult, Task<TNewResult>> next)
497 if (task == null) throw new ArgumentNullException("task");
498 if (next == null) throw new ArgumentNullException("next");
500 var tcs = new TaskCompletionSource<TNewResult>();
501 task.ContinueWith(delegate
503 // When the first task completes, if it faulted or was canceled, bail
504 if (task.IsFaulted) tcs.TrySetException(task.Exception.InnerExceptions);
505 else if (task.IsCanceled) tcs.TrySetCanceled();
506 else
508 // Otherwise, get the next task. If it's null, bail. If not,
509 // when it's done we'll have our result.
510 try { next(task.Result).ContinueWith(t => tcs.TrySetFromTask(t), TaskScheduler.Default); }
511 catch (Exception exc) { tcs.TrySetException(exc); }
513 }, TaskScheduler.Default);
514 return tcs.Task;
516 #endregion